/****************************************************************************
**
** This file is part of the Qt Extended Opensource Package.
**
** Copyright (C) 2008 Trolltech ASA.
**
** Contact: Qt Extended Information (info@qtextended.org)
**
** This file may be used under the terms of the GNU General Public License
** version 2.0 as published by the Free Software Foundation and appearing
** in the file LICENSE.GPL included in the packaging of this file.
**
** Please review the following information to ensure GNU General Public
** Licensing requirements will be met:
**     http://www.fsf.org/licensing/licenses/info/GPLv2.html.
**
****************************************************************************/

#include "phonebookdialog.h"
#include <QPalette>
#include <QDebug>

PhoneBookDialog::PhoneBookDialog(QWidget *parent, Qt::WFlags f)
    : QWidget(parent, f)
{
    setupUi(this);
	
    myPhoneBook = new NeoPhoneBook();
    myPhoneBook->loadPhoneBook();

    selection = 0; // can only be 0 ~ 4
	int index = -1;
	for (int i=0; i<5; i++) {
		if (index <= myPhoneBook->getNumEntries()-1) {
			slotIndex << index++;
		} else {
			slotIndex << -2;
		}
	}

	// set layout
	firstContact->setAlignment(Qt::AlignCenter);
	secondContact->setAlignment(Qt::AlignCenter);
	thirdContact->setAlignment(Qt::AlignCenter);
	forthContact->setAlignment(Qt::AlignCenter);
	fifthContact->setAlignment(Qt::AlignCenter);
	updateScreen(REFRESH);

	// connection
	ringPatternManager = new RingPatternManager();
	QObject::connect(ringPatternManager, SIGNAL(completeSignal()), 
			this, SLOT(finishCallContact()));
	QObject::connect(ringPatternManager, SIGNAL(vibrationSignal(bool, int)), 
			this, SLOT(vibrationListener(bool, int)));
	
	audioManager = new AudioManager();
	QObject::connect(audioManager, SIGNAL(audioFinishingSignal()), 
			this, SLOT(finishCallContact()));	
	loadPattern();

	accelManager = new AccelManager();
	QObject::connect(accelManager, SIGNAL(stateChangeSignal(int)), 
			this, SLOT(stateChangeListener(int)));	

	timer = new QTimer(this);
	timer->setSingleShot(true);
	QObject::connect(timer, SIGNAL(timeout()), this, SLOT(validateMode()));

	//===== Sync: dialog	
	QMenu *menu = QSoftMenuBar::menuFor(this);
	syncAction = new QAction( tr("Sync"), this);
	menu->addAction(syncAction);
	connect(syncAction, SIGNAL(triggered()),this, SLOT(showSyncDialog()));
	

	syncManager = new SyncManager();
	syncDialog = new SyncDialog();
	connect(syncDialog, SIGNAL(statusSignal(int)), this, SLOT(updateMenu(int)));
	connect(syncManager, SIGNAL(statusSignal(int)), syncDialog, SLOT(updateStatus(int)));
	connect(syncDialog, SIGNAL(connectSignal(QString, QString)),
			syncManager, SLOT(sync(QString, QString)));
	connect(syncDialog, SIGNAL(disconnectSignal()),
			syncManager, SLOT(disconnectionServlet()));

	//===== Sync: send action
	connect(this, SIGNAL(addContactSignal(NeoPhoneBookEntry*)),
			syncManager, SLOT(addContactListener(NeoPhoneBookEntry*)));
	connect(this, SIGNAL(replaceContactSignal(int , NeoPhoneBookEntry*)),
			syncManager, SLOT(replaceContactListener(int, NeoPhoneBookEntry*)));
	connect(this, SIGNAL(deleteContactSignal(int)),
			syncManager, SLOT(deleteContactListener(int)));
	connect(this, SIGNAL(callContactSignal(int)),
			syncManager, SLOT(callContactListener(int)));
	connect(this, SIGNAL(stopCallContactSignal()),
			syncManager, SLOT(stopCallContactListener()));

	//===== Sync: recv action
	connect(syncManager, SIGNAL(addContactSignal(NeoPhoneBookEntry*)),
			this, SLOT(addContactListener(NeoPhoneBookEntry*)));
	connect(syncManager, SIGNAL(replaceContactSignal(int , NeoPhoneBookEntry*)),
			this, SLOT(replaceContactListener(int, NeoPhoneBookEntry*)));
	connect(syncManager, SIGNAL(deleteContactSignal(int)),
			this, SLOT(deleteContactListener(int)));
	connect(syncManager, SIGNAL(callContactSignal(int)),
			this, SLOT(callContactListener(int)));
	connect(syncManager, SIGNAL(stopCallContactSignal()),
			this, SLOT(stopCallContactListener()));
}

PhoneBookDialog::~PhoneBookDialog()
{
    // Do nothing for now
}

/**
 * Update the contact slot at 'index' with the text 'name'.
 * @param index the index of contact slot from 0 to 4.
 * @param name the contact name to show.
 */
void PhoneBookDialog::updateContactSlot(int index, QString name)
{    
	switch (index) {
	case 0:
		firstContact->setText(name);
		if (selection == index) {
			firstContact->setPalette(QPalette(Qt::blue));
		} else {
			firstContact->setPalette(QPalette(Qt::transparent));
		}
		return;
	case 1:
		secondContact->setText(name);
		if (selection == index) {
			secondContact->setPalette(QPalette(Qt::blue));
		} else {
			secondContact->setPalette(QPalette(Qt::transparent));
		}
		return;
	case 2:
		thirdContact->setText(name);
		if (selection == index) {
			thirdContact->setPalette(QPalette(Qt::blue));
		} else {
			thirdContact->setPalette(QPalette(Qt::transparent));
		}
		return;
	case 3:
		forthContact->setText(name);
		if (selection == index) {
			forthContact->setPalette(QPalette(Qt::blue));
		} else {
			forthContact->setPalette(QPalette(Qt::transparent));
		}
		return;
	case 4:
		fifthContact->setText(name);
		if (selection == index) {
			fifthContact->setPalette(QPalette(Qt::blue));
		} else {
			fifthContact->setPalette(QPalette(Qt::transparent));
		}
		return;
	default:
		qDebug() << "updateContactSlot: Invalid index";
		return;
	}
}

void PhoneBookDialog::updateScreen(Update_Types type)
{
    // updates the 5 contact slots for scrolling or refreshing
	switch (type) {
	case UP:
		if (selection == 0) return;
		if (selection > 1) {
			selection--;
		} else {
			if (slotIndex.at(0) == -1) {
				selection--;
			} else {
					int top = slotIndex.at(4);
				for (int i=4; i>-1; i--) {
					slotIndex.replace(i, --top);
				}
			}
		}
		updateScreen(REFRESH);
		return;

	case DOWN:
		if (selection == 4) return;
		if (selection < 3 && slotIndex.at(selection + 1) != -2) {
			selection++;
		} else { // selection == 3
			if (slotIndex.at(selection + 1) == -2) return;
			if (slotIndex.at(4) == myPhoneBook->getNumEntries() - 1) {
				selection++;				
			} else {
				int bottom = slotIndex.at(0);
				for (int i=0; i<5; i++) {				
					slotIndex.replace(i, ++bottom);
				}
			}			
		}		
		updateScreen(REFRESH);
		return;

	case REFRESH:
		for (int i=0; i<5; i++) {
			int index = slotIndex.at(i);
			if (index == -1) {
				updateContactSlot(i, "New Contact");
			} else {				
				if (index == -2) {
					updateContactSlot(i, "");
				} else {
					NeoPhoneBookEntry *entry = myPhoneBook->getElementAt(index);
					updateContactSlot(i, entry->getContactName());
				}				
			}
		}		
		return;

	case DELETE_LAST:
		return;

	default:
		return;
	}
}

void PhoneBookDialog::addContact(NeoPhoneBookEntry *newEntry, bool signal)
{
    // add contact to the phonebook and refresh screen
	//qDebug() << "add" <<newEntry->getContactName();
	int index = myPhoneBook->addEntry(newEntry);
	if (signal) emit addContactSignal(newEntry);
	qDebug() << "add at"<<index;
	if (slotIndex.contains(-2)) {
		qDebug() << "update slotIndex";
		if (slotIndex.contains(index)) {
			for (int i=1; i<5; i++) {
				if (slotIndex.at(i)==-2) {
					slotIndex.replace(i, slotIndex.at(i-1)+1);
					break;
				}
			}
		} else {
			slotIndex.replace(4, index);
			for (int i=3; i>0; i--) {
				int index2 = slotIndex.at(i);
				if (index2 == -2) {
					slotIndex.replace(i, index);
					slotIndex.replace(i+1, -2);
				} else if (index2 > index) {
					slotIndex.replace(i, index);
					slotIndex.replace(i+1, index2);
				} else if (index2 == index) {
					slotIndex.replace(i+1, index+1);
				} else {
					break;
				}
			}
		}		
	}
	updateScreen(REFRESH);
	jumpTo(index);
}

void PhoneBookDialog::replaceContact(NeoPhoneBookEntry *newEntry, bool signal)
{
    // replace contact in the phonebook and refresh screen
	//qDebug() << "replace" << newEntry->getContactName();
	int index = slotIndex.at(selection);
	int newIndex = myPhoneBook->replaceEntry(index, newEntry);
	if (signal) emit replaceContactSignal(index, newEntry);
	updateScreen(REFRESH);
	jumpTo(newIndex);
}

void PhoneBookDialog::deleteContact()
{	
	int index = slotIndex.at(selection);
	deleteContact(index);
}

void PhoneBookDialog::deleteContact(int index, bool signal)
{
    // delete contact in the phonebook
	int maxIndex;
	myPhoneBook->deleteEntry(index);
	if (signal) emit deleteContactSignal(index);

	if (selection == 4) { // at the end
		if (slotIndex.at(0) == -1) {
			selection--;
			slotIndex.replace(4, -2);	
		} else {
			for (int i=0; i<5; i++) {
				slotIndex.replace(i, slotIndex.at(i)-1);
			}
		}
	} else { 
		maxIndex = myPhoneBook->getNumEntries() - 1;
		// back not ok
		if ((slotIndex.at(selection)+(4-selection)) > maxIndex) {
			if (slotIndex.at(0) == -1) { // front not ok
				for (int i=1; i<=(maxIndex+1); i++) {
					slotIndex.replace(i, i-1);
				}
				for (int i=(maxIndex+2); i<5; i++) {
					slotIndex.replace(i, -2);
				}
				if (slotIndex.at(selection)==-2) selection--;
			} else { // front ok
				for (int i=0; i<5; i++) {
					slotIndex.replace(i, slotIndex.at(i)-1);
				}
			}	
		}
	}
	qDebug()<<slotIndex.at(0)<<slotIndex.at(1)
			<<slotIndex.at(2)<<slotIndex.at(3)<<slotIndex.at(4);
	updateScreen(REFRESH);
}

void PhoneBookDialog::on_searchLineEdit_textChanged(const QString &text)
{
        if (text.isEmpty()) return;
        int index = myPhoneBook->searchContact(text);
        jumpTo(index);
        //qDebug()<<"search for"<<text;
}

void PhoneBookDialog::on_upButton_clicked()
{
        searchLineEdit->setText("");
        updateScreen(UP);
}

void PhoneBookDialog::on_downButton_clicked()
{
        searchLineEdit->setText("");
        updateScreen(DOWN);
}

void PhoneBookDialog::on_selectButton_clicked()
{		
	qDebug() << "PhoneBookDialog::on_selectButton_clicked() |";
	int index = slotIndex.at(selection);
	if (index == -1) { 
		openAddContactDialog();
	} else { 
		ViewContactDialog *myViewContactDialog = new ViewContactDialog();
		myViewContactDialog->setAttribute(Qt::WA_DeleteOnClose);
		NeoPhoneBookEntry *entry = myPhoneBook->getElementAt(index);
		myViewContactDialog->setupView(entry);
		QObject::connect(myViewContactDialog, SIGNAL(editContactSignal()), 
				this, SLOT(openAddContactDialog()));
		QObject::connect(myViewContactDialog, SIGNAL(deleteContactSignal()), 
				this, SLOT(deleteContact()));
		QObject::connect(myViewContactDialog, SIGNAL(callContactSignal()), 
				this, SLOT(callContact()));
		QObject::connect(myViewContactDialog, SIGNAL(stopCallContactSignal()), 
				this, SLOT(stopCallContact()));
		QObject::connect(this, SIGNAL(closeSignal(int)), 
						myViewContactDialog, SLOT(closeListener(int)));
		// for testing ring pattern
		QObject::connect(this, SIGNAL(vibrationSignal(bool, int)), 
				myViewContactDialog, SLOT(vibrationListener(bool, int)));
		// for testing accelerometer
		QObject::connect(myViewContactDialog, SIGNAL(stateChangeSignal(int)), 
				accelManager, SLOT(stateChangeListener(int)));

		QString audioname = entry->getRingToneFilePath();
		if (!audioname.endsWith(".aud")) {
			audioname = audioname + ".aud";
		}
		audioManager->setAudio(audioname);
		myViewContactDialog->setSlider(audioManager->getDuration());
		QObject::connect(audioManager, SIGNAL(audioPlayingSignal(int)), 
			myViewContactDialog, SLOT(audioPlayingListenser(int)));
		myViewContactDialog->showMaximized();
	}

}

/**
 * Prompt user to be able to delete the selected contact
 */
void PhoneBookDialog::on_deleteButton_clicked()
{	
        if (slotIndex.at(selection) == -1) return;

        QMessageBox msgBox;
        msgBox.setWindowTitle("Delete Contact");
        msgBox.setIcon(QMessageBox::Information);
        msgBox.setText("Do you really want to do that?");
        msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
        switch (msgBox.exec()) {	
        case QMessageBox::Yes:
                deleteContact(slotIndex.at(selection));
                break;
        case QMessageBox::No:
        break;
    	default:
        	break;
    	}
}

void PhoneBookDialog::openAddContactDialog()
{	
        int index = slotIndex.at(selection);
        if (index == -1) {
                AddContactDialog *myAddContactDialog = new AddContactDialog();
                myAddContactDialog->setAttribute(Qt::WA_DeleteOnClose);
                QObject::connect(myAddContactDialog, 
                                SIGNAL(updateContactSignal(NeoPhoneBookEntry*)), 
                                this, SLOT(addContact(NeoPhoneBookEntry*)));
				QObject::connect(this, SIGNAL(closeSignal(int)), 
						myAddContactDialog, SLOT(closeListener(int)));
                myAddContactDialog->showMaximized();
        } else {
                AddContactDialog *myAddContactDialog = new AddContactDialog();
                myAddContactDialog->setAttribute(Qt::WA_DeleteOnClose);
                myAddContactDialog->setupEdit(myPhoneBook->getElementAt(index));
                QObject::connect(myAddContactDialog, 
                                SIGNAL(updateContactSignal(NeoPhoneBookEntry*)), 
                                this, SLOT(replaceContact(NeoPhoneBookEntry*)));
				QObject::connect(this, SIGNAL(closeSignal(int)), 
						myAddContactDialog, SLOT(closeListener(int)));
                myAddContactDialog->showMaximized();
        }	
}

void PhoneBookDialog::jumpTo(int jumpToIndex)
{	
        int currentIndex = slotIndex.at(selection);
        while (currentIndex != jumpToIndex) {	
                if (currentIndex > jumpToIndex) {
                        updateScreen(UP);
                } else { // currentIndex < jumpToIndex
                        updateScreen(DOWN);
                }
                currentIndex = slotIndex.at(selection);
        } 
}

void PhoneBookDialog::callContact(bool signal)
{
	qDebug() << "PhoneBookDialog->callContact()";	
	accelManager->start();

	int index = slotIndex.at(selection);
    NeoPhoneBookEntry *entry = myPhoneBook->getElementAt(index);
	ringPatternManager->setPattern(entry->getVibrationPatternFilePath(), 
	RingPatternManager::VIBRATION);
	ringPatternManager->setPattern(entry->getLedPatternFilePath(), 
	RingPatternManager::LED);

    ringPatternManager->setSync(entry->getRingOption());

	QString audioname = entry->getRingToneFilePath();
	if (!audioname.endsWith(".aud")) {
		audioname = audioname + ".aud";
	}
	audioManager->stop();
	audioManager->setAudio(audioname);

	
	mode = accelManager->getCurrentState();
	while ( mode == AccelManager::UNKNOWN) {
		//qDebug() << "Mode" << mode;
		mode = accelManager->getCurrentState();
	}

	switch (mode) {
	case NORMAL:
		deviceCount = 2;
		audioManager->play(entry->getRingOffset());
		ringPatternManager->playPattern();
		break;
	case SILENT:
		deviceCount = 1;
		ringPatternManager->playPattern();
		break;
	default:
		qDebug() << "unsupported mode";
	}
	if (signal) emit callContactSignal(index);
}


void PhoneBookDialog::stopCallContact(bool signal)
{
	qDebug() << "PhoneBookDialog->stopCallContact()";
	if (signal) emit stopCallContactSignal();
	ringPatternManager->stopVibrate();
	audioManager->stop();
	accelManager->stop();
}

void PhoneBookDialog::loadPattern() 
{
        QDir dir = QDir::home(); // QDir::current()
        dir.cd("Documents");
        dir.setFilter(QDir::Files | QDir::Hidden | QDir::NoSymLinks);
        dir.setSorting(QDir::Name);	
        QFileInfoList fileInfoList = dir.entryInfoList();
        for (int i=0; i < fileInfoList.size(); i++) {
                QFileInfo fileInfo = fileInfoList.at(i);
                //qDebug() << fileInfo.fileName();
                if (fileInfo.fileName().endsWith(".vib")) {
			ringPatternManager->addPattern(fileInfo.fileName(),
					RingPatternManager::VIBRATION);
		} else if (fileInfo.fileName().endsWith(".led")) {
			ringPatternManager->addPattern(fileInfo.fileName(),
					RingPatternManager::LED);
		}
	}
}

void PhoneBookDialog::finishCallContact()
{
	qDebug() << "PhoneBookDialog->finishCallContact()";
	deviceCount--;
	qDebug() << deviceCount << "device(s) remaining";
	if (deviceCount==0) {		
		qDebug() << "no devices running, stop accelManager";
		accelManager->stop();
	}
}

void PhoneBookDialog::vibrationListener(bool pos, int id)
{
	//qDebug()<<"phonebookdialog vibration"<<id;
	emit vibrationSignal(pos, id);
}

void PhoneBookDialog::stateChangeListener(int state)
{
	switch (state) {
	case AccelManager::UP:
		qDebug() << "setup timer";
		hasStateChange = false;
		timer->stop();
		timer->start(2000);
		break;
	case AccelManager::DOWN:
		hasStateChange = true;
		if (mode == NORMAL) {
			qDebug() << "hang up the phone...";
			stopCallContact();
		}
		break;
	default:
		qDebug() << "unsupported state";
	}
}

void PhoneBookDialog::validateMode()
{
	qDebug() << "Validate mode...";
	if (!hasStateChange) {
		mode = NORMAL;
		qDebug() << "Change mode to NORMAL";
	} else {
		qDebug() << "Remain in mode SILENT";
	}	
}

void PhoneBookDialog::showSyncDialog()
{
	qDebug() << "PhoneBookDialog::showSyncDialog() |";
	syncDialog->showMaximized();
}

void PhoneBookDialog::addContactListener(NeoPhoneBookEntry *entry) 
{
	emit closeSignal(0);
	addContact(entry, false);
}
void PhoneBookDialog::replaceContactListener(int contactIndex, NeoPhoneBookEntry *entry) {
	emit closeSignal(0);
	jumpTo(contactIndex);
	replaceContact(entry, false);
}
void PhoneBookDialog::deleteContactListener(int contactIndex) 
{
	emit closeSignal(0);
	deleteContact(contactIndex, false);
}
void PhoneBookDialog::callContactListener(int contactIndex)
{
	stopCallContact(false);
	emit closeSignal(1);
	jumpTo(contactIndex);
	on_selectButton_clicked();
	callContact(false);
}
void PhoneBookDialog::stopCallContactListener() {
	stopCallContact(false);
}
void PhoneBookDialog::updateMenu(int status)
{
	switch (status) {
	case SyncManager::SYNCED:
		syncAction->setText("Unsync");
		syncAction->disconnect(SIGNAL(triggered()));
		connect(syncAction, SIGNAL(triggered()), this, SLOT(unsync()));
		QMessageBox::information(this, "Sync Info.",
				"Synchronised with remote host.", QMessageBox::Ok);
		break;
	case SyncManager::DISCONNECT:
		syncAction->setText("Sync");
		syncAction->disconnect(SIGNAL(triggered()));
		connect(syncAction, SIGNAL(triggered()), this, SLOT(showSyncDialog()));
		QMessageBox::information(this, "Sync Info.",
				"Disconnect from remote host.", QMessageBox::Ok);
		break;
	case SyncManager::ERROR_NETWORK:
		syncAction->setText("Sync");
		syncAction->disconnect(SIGNAL(triggered()));
		connect(syncAction, SIGNAL(triggered()), this, SLOT(showSyncDialog()));
		QMessageBox::information(this, "Sync Info.",
				"Unable to connect to remote host.", QMessageBox::Ok);
		break;
	default:
		qDebug() << "PhoneBookDialog::updateMenu | Unknown status" << status;
	}
	
}
void PhoneBookDialog::unsync() 
{	
	syncDialog->on_syncButton_clicked();
}

